package com.sloth.bear.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sloth.bear.constant.LayoutType;
import com.sloth.bear.dto.entity.Doc;
import com.sloth.bear.dto.entity.DocItem;
import com.sloth.bear.dto.request.*;
import com.sloth.bear.dto.vo.DocDetailsVo;
import com.sloth.bear.dto.vo.DocListVo;
import com.sloth.bear.mapper.DocMapper;
import com.sloth.bear.service.DocItemService;
import com.sloth.bear.service.DocService;
import com.sloth.bear.util.AssertUtils;
import com.sloth.bear.util.BaseHelper;
import com.sloth.bear.util.DateUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 文档服务实现类
 */
@RequiredArgsConstructor
@Service
public class DocServiceImpl extends ServiceImpl<DocMapper, Doc> implements DocService {

    private final DocItemService docItemService;

    @Override
    public Long createDoc(DocRequest request) {
        // 验证父文档是否存在
        if (request.getParentId() != null) {
            Doc parent = getById(request.getParentId());
            AssertUtils.notNull(parent, "父文档不存在");
        }

        Doc doc = BaseHelper.r2t(request, Doc.class);
        initDoc(doc);

        boolean result = save(doc);
        AssertUtils.isTrue(result, "文档创建失败");
        return doc.getId();
    }

    @Override
    public List<DocListVo> listDoc(DocListRequest request) {
        List<Doc> docs = list(Wrappers.<Doc>lambdaQuery()
                .eq(request.getNamespaceId() != null, Doc::getNamespaceId, request.getNamespaceId())
                .eq(request.getIsFavorite() != null, Doc::getIsFavorite, request.getIsFavorite())
                .orderByAsc(Doc::getSortNo)
                .orderByDesc(Doc::getCreatedTime));

        if (Boolean.TRUE.equals(request.getIsFavorite())) {
            return docs.stream().map(this::entityToVo).collect(Collectors.toList());
        } else {
            return buildTreeStructure(docs, null);
        }
    }

    private List<DocListVo> buildTreeStructure(List<Doc> allDocuments, Long parentId) {
        List<DocListVo> result = new ArrayList<>();

        // 先过滤出符合条件的文档并按sortNo排序
        allDocuments.stream()
                .filter(doc -> (parentId == null && doc.getParentId() == null) ||
                        (parentId != null && parentId.equals(doc.getParentId())))
                .sorted(Comparator.comparing(document -> document.getSortNo() != null ? document.getSortNo() : 0))
                .forEach(document -> {
                    DocListVo vo = entityToVo(document);
                    List<DocListVo> children = buildTreeStructure(allDocuments, document.getId());
                    vo.setChildren(children);
                    result.add(vo);
                });
        return result;
    }

    private DocListVo entityToVo(Doc doc) {
        DocListVo vo = BaseHelper.r2t(doc, DocListVo.class);
        vo.setCreatedTime(DateUtils.getTimeDifference(doc.getCreatedTime()));
        return vo;
    }


    @Override
    public DocDetailsVo detailsDoc(Long id) {
        Doc doc = getEntity(id);
        DocDetailsVo vo = BaseHelper.r2t(doc, DocDetailsVo.class);
        // 加载关联的子项
        vo.setItems(docItemService.getItemsByDocumentId(id));
        // 加载关联的标签
        return vo;
    }

    private Doc getEntity(Long id) {
        Doc doc = getById(id);
        AssertUtils.notNull(doc, "文档不存在");
        return doc;
    }

    private void initDoc(Doc document) {
        if (document.getLayoutType() == null) {
            document.setLayoutType(LayoutType.GRAD);
        }
    }

    @Override
    public void updateDoc(Long id, DocUpdateRequest request) {
        Doc doc = getEntity(id);

        // 验证父文档是否存在
        if (request.getParentId() != null && !request.getParentId().equals(doc.getParentId())) {
            Doc parent = getById(request.getParentId());
            AssertUtils.notNull(parent, "父文档不存在");
        }

        BeanUtils.copyProperties(request, doc);
        initDoc(doc);
        boolean result = updateById(doc);
        AssertUtils.isTrue(result, "文档更新失败");
    }

    @Override
    public void dropDoc(Long id, DragRequest request) {
        if ("inner".equals(request.getDropType())) {
            // 节点拖入另一个节点
            update(Wrappers.<Doc>lambdaUpdate()
                    .eq(Doc::getId, id)
                    .set(Doc::getParentId, request.getTargetId()));
        } else if ("before".equals(request.getDropType()) || "after".equals(request.getDropType())) {
            // 节点拖到另一个节点前面：parentId等于另一个节点，顺序在另一个节点前
            Doc dropNode = getById(request.getTargetId());

            update(Wrappers.<Doc>lambdaUpdate()
                    .eq(Doc::getId, id)
                    .set(Doc::getParentId, dropNode.getParentId()));

            List<Doc> docNotes = list(Wrappers.<Doc>lambdaQuery()
                    .isNull(dropNode.getParentId() == null, Doc::getParentId)
                    .eq(dropNode.getParentId() != null, Doc::getParentId, dropNode.getParentId())
                    .orderByAsc(Doc::getSortNo)
                    .orderByDesc(Doc::getCreatedTime));

            int sourceIndex = 0;
            int targetIndex = 0;
            Doc dropDoc = null;
            for (int i = 0; i < docNotes.size(); i++) {
                if (docNotes.get(i).getId().equals(id)) {
                    sourceIndex = i;
                    dropDoc = docNotes.get(i);
                } else if (docNotes.get(i).getId().equals(request.getTargetId())) {
                    targetIndex = i;
                }
            }

            int index;
            if ("before".equals(request.getDropType())) {
                index = targetIndex > sourceIndex ? (targetIndex - 1) : targetIndex;
            } else {
                index = targetIndex;
            }

            docNotes.remove(sourceIndex);
            docNotes.add(index, dropDoc);

            for (int i = 0; i < docNotes.size(); i++) {
                update(Wrappers.<Doc>lambdaUpdate()
                        .eq(Doc::getId, docNotes.get(i).getId())
                        .set(Doc::getSortNo, i));
            }
        }
    }

    @Override
    public void deleteDoc(Long id) {
        long childCount = count(Wrappers.<Doc>lambdaQuery().eq(Doc::getParentId, id));
        AssertUtils.isTrue(childCount == 0, "文档存在子文档，不能删除");

        long count = docItemService.count(Wrappers.<DocItem>lambdaQuery().eq(DocItem::getDocumentId, id));
        AssertUtils.isTrue(count == 0, "文档存在子文档，不能删除");

        boolean result = removeById(id);
        AssertUtils.isTrue(result, "文档删除失败");
    }
}